package org.sdnstproject.interlayer.DummyProxyV10;

import com.google.common.primitives.Longs;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import org.projectfloodlight.openflow.exceptions.OFParseError;
import org.projectfloodlight.openflow.protocol.*;
import org.projectfloodlight.openflow.types.U16;
import org.projectfloodlight.openflow.types.U32;
import org.sdnstproject.interlayer.OF10Protocal.*;
import org.sdnstproject.interlayer.interlayerProxy.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;

public class DummyControllerV10 extends Thread {
    private static final Logger log = LoggerFactory.getLogger(DummyControllerV10.class);
    public static final int MINIMUM_LENGTH = 8;

    public ServerSocket serverSock;
    public volatile Socket targetSock;
    public volatile InputStream in;
    public volatile OutputStream out;
    /* for OF message */
    private OFVersion version = OFVersion.OF_10;
    private OFFactory factory = OFFactories.getFactory(OFVersion.OF_10);;
    private OFMessageReader<OFMessage> reader = factory.getReader();

    private volatile long requestXid = 0xeeeeeeeel;
    public volatile long responseXid = 0x0L;
    private long startXid = 0xffffffff;
    private boolean handshaked = false;
    private Configuration config = Configuration.getInstance();
    private int port = Integer.parseInt(config.getInterlayerPort());
    private OFFlowAdd backupFlowAdd;
    public volatile boolean strategySend = false;
    public volatile boolean running = true;
    public OFState state = new OFState();
    private Strategy strategy;
    private ResultAnalyzer analyzer = ResultAnalyzer.getInstance();
    public HashSet<OFType> needSynMsg = new HashSet<OFType>();

    public DummyControllerV10() {
        addSynMsg();

    }
    public DummyControllerV10(Strategy strategy) {
        this.strategy =  strategy;
        addSynMsg();
    }

    public void notifyOFFuzzer() {
        strategySend = true;
        synchronized (this) {
            notify();
        }
    }

    public void listeningSwitch() throws Exception {
        log.info("[DummyController] Listening switches on " + this.port);
        serverSock = new ServerSocket(port);
        serverSock.setReuseAddress(true);
        targetSock = serverSock.accept();
        log.info("[DummyController] Switch connected from  " + targetSock.toString());
        in = targetSock.getInputStream();
        out = targetSock.getOutputStream();
    }

    @Override
    public void run() {
        byte[] recv;
        int readlen;
        try {
            while (running && !Thread.currentThread().isInterrupted()) {
                recv = new byte[8192];
                int mlen;
                int len = 0;
                if ((readlen = in.read(recv, 0, recv.length)) != -1) {
                    byte[] lenarr = {recv[2], recv[3]};
                    mlen = Integer.parseInt(javax.xml.bind.DatatypeConverter.printHexBinary(lenarr), 16);
                    ByteBuf newrecv = Unpooled.directBuffer(mlen);
                    newrecv.writeBytes(recv);
                    len = len + readlen;
                    newrecv.writerIndex(len);

                    while(mlen > len){
                        if((readlen = in.read(recv, 0, recv.length)) != -1){
                            newrecv.writeBytes(recv);
                            len = len + readlen;
                            newrecv.writerIndex(len);
                        }
                    }

                    parseOFMsg(newrecv, mlen);
//                    parseOFMsg(recv, readlen);
                } else {
                    log.info("switch has close socket.");
                    if(in != null) in.close();
                    if(out != null) out.close();
//                    serverSock.close();
                    targetSock.close();
                    break; // end of connection
                }
            }
        } catch (SocketException e) {
            log.info(e.toString());
        }catch (Exception e) {
            currentThread().interrupt();
            e.printStackTrace();
        } finally {
            try {
                in.close();
                out.close();
//                serverSock.close();
                targetSock.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    public boolean parseOFMsg(ByteBuf bb, int len) {
        int totalLen = bb.readableBytes();
        int offset = bb.readerIndex();
        while (offset < totalLen) {
            bb.readerIndex(offset);

            bb.readByte();
            bb.readByte();
            int length = U16.f(bb.readShort());
            bb.readerIndex(offset);

            if (length < MINIMUM_LENGTH) {
                log.info("Wrong length: Expected to be >= " + MINIMUM_LENGTH + ", was: " + length);
                return false;
            }

            OFMessage message = null;
            try {
                message = reader.readFrom(bb);
            } catch (OFParseError e) {
                analyzer.isReceiveErrorMsg = true;
                log.info(e.toString());
                notifyOFFuzzer();
                return true;
            }
            responseXid = message.getXid();
            //if get correct response xid, then notify the state.
            if(responseXid == requestXid) {
                synchronized (state) {
                    state.notify();
                }
            }else if(strategy.field.equals("xid")) {
                if(Long.parseLong(strategy.actionValue) == responseXid) {
                    synchronized (state) {
                        state.notify();
                    }
                }
            }
            // print message.
            log.info(message.toString());
            //process OpenFlow message.
            state.processOFMsg(message);

            offset += length;
        }

        bb.clear();
        return true;
    }

    public class OFState extends Thread{
        void processOFMsg(OFMessage m) {
            switch(m.getType()) {
//			case HELLO:
//				processOFHello((OFHello)m);
//				break;
                case FEATURES_REPLY:
                    processOFFeaturesReply((OFFeaturesReply)m);
                    break;
                case GET_CONFIG_REPLY:
                    processOFGetConfigReply((OFGetConfigReply)m);
                    break;
                case BARRIER_REPLY:
                    processOFBarrierReply((OFBarrierReply)m);
                    break;
                case STATS_REPLY:
                    processOFStatsReply((OFStatsReply)m);
                    break;
                case EXPERIMENTER:
                    processOFExperimenter((OFExperimenter)m);
                    break;
                case ECHO_REQUEST:
                    processOFEchoRequest((OFEchoRequest)m);
                    break;
                case ECHO_REPLY:
                    processOFEchoReply((OFEchoReply)m);
                    break;
                case ERROR:
                    processOFError((OFErrorMsg)m);
                    break;
                case FLOW_REMOVED:
                    processOFFlowRemoved((OFFlowRemoved)m);
                    break;
                case ROLE_REPLY:
                    processOFRoleReply((OFRoleReply)m);
                    break;
                case PACKET_IN:
                    processOFPacketIn((OFPacketIn)m);
                    break;
                case PORT_STATUS:
                    processOFPortStatus((OFPortStatus)m);
                    break;
            }
        }

        private void processOFPortStatus(OFPortStatus m) {
            log.info("Reveived a OFPortStatus.");
        }

        private void processOFPacketIn(OFPacketIn m) {
            log.info("Reveived a OFPacketIn.");
        }

        private void processOFRoleReply(OFRoleReply m) {
            log.info("Reveived a OFRoleReply.");
        }

        private void processOFFlowRemoved(OFFlowRemoved m) {
            log.info("Reveived a OFFlowRemoved.");
        }

        private void processOFError(OFErrorMsg m) {
            log.info("Reveived a OFErrorMsg.");
        }

        private void processOFEchoReply(OFEchoReply m) {
            log.info("Reveived a OFEchoReply.");
        }

        private void processOFEchoRequest(OFEchoRequest m) {
            log.info("Reveived a OFEchoRequest.");
            if(strategy != null && strategy.ofType == OFType.ECHO_REPLY) {
                switch (strategy.action) {
                    case MODIFY:
                        FuEchoReplyV10 echoReply = new FuEchoReplyV10();
                        echoReply.setField(strategy.field, strategy.actionValue);
                        sendRawMsg(echoReply.writeTo());
                        notifyOFFuzzer();
                        log.info("send a ECHO_REPLY to switch.");
                        break;
                    case DELAY:
                        try {
                            Thread.sleep(Long.parseLong(strategy.actionValue));
                        } catch (Exception e) {
                            log.info(e.toString());
                        }
                        sendEchoReply(m.getXid());
                        log.info("send a ECHO_REPLY to switch.");
                        notifyOFFuzzer();
                        break;
                    case DUPLICATE:
                        for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                            sendEchoReply(m.getXid());
                        }
                        log.info("send "+strategy.actionValue+" ECHO_REPLY to switch.");
                        notifyOFFuzzer();
                        break;
                    case DROP:
                        notifyOFFuzzer();
                        return;
                }
            }else {
                sendEchoReply(m.getXid());
                log.info("send a ECHO_REPLY to switch.");
            }

        }

        private void processOFExperimenter(OFExperimenter m) {
            log.info("Reveived a OFExperimenter.");
            sendExperimenter(m.getXid());
            log.info("send a OFExperimenter to switch.");
        }

        private void processOFStatsReply(OFStatsReply m) {
            log.info("Reveived a OFStatsReply.");
        }

        private void processOFGetConfigReply(OFGetConfigReply m) {
            log.info("Reveived a OFGetConfigReply.");
        }

        private void processOFBarrierReply(OFBarrierReply m) {
            log.info("Reveived a OFGetConfigReply.");
        }

        private void processOFFeaturesReply(OFFeaturesReply m) {
            log.info("Reveived a OFFeaturesReply.");
            handshaked = true;
            if(strategy != null && needSynMsg.contains(strategy.ofType)) {
                state.start();
                log.info("asynchronous state machine has start.");
            }else {
                log.info("needSynMsg not contains(strategy.ofType)");
            }
        }

        public void processOFHello(OFHello m) {
            //send hello
            if(strategy != null && strategy.ofType == OFType.HELLO) {
                switch (strategy.action) {
                    case MODIFY:
                        FuHelloV10 hello = new FuHelloV10();
                        hello.setField(strategy.field, strategy.actionValue);
                        sendRawMsg(hello.writeTo());
                        notifyOFFuzzer();
                        log.info("send a HELLO to switch.");
                        break;
                    case DELAY:
                        try {
                            Thread.sleep(Long.parseLong(strategy.actionValue));
                        } catch (Exception e) {
                            log.info(e.toString());
                        }
                        sendHello(0);
                        log.info("send a HELLO to switch.");
                        notifyOFFuzzer();
                        break;
                    case DUPLICATE:
                        for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                            sendHello(0);
                        }
                        log.info("send "+strategy.actionValue+" HELLO to switch.");
                        notifyOFFuzzer();
                        break;
                    case DROP:
                        notifyOFFuzzer();
                        return;
                }
            }else {
                sendHello(0);
            }

            //send FeatureReply
            if(strategy != null && strategy.ofType == OFType.FEATURES_REQUEST) {
                switch (strategy.action) {
                    case MODIFY:
                        FuFeaturesRequestV10 featuresRequest = new FuFeaturesRequestV10();
                        featuresRequest.xid = requestXid;
                        featuresRequest.setField(strategy.field, strategy.actionValue);
                        sendRawMsg(featuresRequest.writeTo());
                        notifyOFFuzzer();
                        log.info("send a FEATURES_REPLY to switch.");
                        break;
                    case DELAY:
                        try {
                            Thread.sleep(Long.parseLong(strategy.actionValue));
                        } catch (Exception e) {
                            log.info(e.toString());
                        }
                        try {
                            sendFeatureReq(requestXid);
                        } catch (OFParseError e) {
                            e.printStackTrace();
                        }
                        log.info("send a FEATURES_REPLY to switch.");
                        notifyOFFuzzer();
                        break;
                    case DUPLICATE:
                        for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                            try {
                                sendFeatureReq(requestXid);
                            } catch (OFParseError e) {
                                e.printStackTrace();
                            }
                        }
                        log.info("send "+strategy.actionValue+" FEATURES_REPLY to switch.");
                        notifyOFFuzzer();
                        break;
                    case DROP:
                        notifyOFFuzzer();
                        return;
                }
            }else {
                try {
                    sendFeatureReq(requestXid);
                } catch (OFParseError e) {
                    e.printStackTrace();
                }
                log.info("send a FEATURES_REPLY to switch.");
            }
        }

        @Override
        public void run() {
            switch (strategy.ofType) {
                case PACKET_OUT:
                    sendPACKETOUTtoController();
                    break;
                case FLOW_MOD:
                    sendFLOWMODtoController();
                    break;
                case PORT_MOD:
                    sendPORTMODtoController();
                    break;
                case SET_CONFIG:
                    sendSETCONFIGtoController();
                    break;
                case BARRIER_REQUEST:
                    sendBarrierRequesttoController();
                    break;
                case GET_CONFIG_REQUEST:
                    sendGetConfigRequesttoController();
                    break;
                case QUEUE_GET_CONFIG_REQUEST:
                    sendQueueGetConfigRequesttoController();
                    break;
                case ECHO_REQUEST:
                    sendECHOREQUESTtoController();
                    break;
                case ERROR:
                    sendERRORtoController();
                    break;
                case STATS_REQUEST:
                    sendStatReqtoController();
                    break;
            }
            strategySend = true;
            //after send syn stratege msg. waitting 3sec to get response msg or waitting to been notified.
            synchronized (this) {
                if(responseXid != requestXid) {
                    try {
                        wait(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            if(responseXid == requestXid) {
                log.info("responseXid == requestXid");
                analyzer.notReceiveResponseMsg = false;
            }else {
                log.info("responseXid != requestXid");
                analyzer.notReceiveResponseMsg = true;
            }
            //notify ResultAnalyzer to get result.
            notifyOFFuzzer();
        }
    }

    public void sendPACKETOUTtoController() {
        switch (strategy.action) {
            case MODIFY:
                FuPacketOutV10 packetOut = new FuPacketOutV10();
                packetOut.xid = requestXid;
                packetOut.setField(strategy.field, strategy.actionValue);
                sendRawMsg(packetOut.writeTo());
                log.info("send a PACKET_OUT to switch.");
                break;
            case DELAY:
                try {
                    Thread.sleep(Long.parseLong(strategy.actionValue));
                } catch (Exception e) {
                    log.info(e.toString());
                }
                sendRawMsg(new FuPacketOutV10().setXid(requestXid).writeTo());
                log.info("send a PACKET_OUT to switch.");
                break;
            case DUPLICATE:
                for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                    sendRawMsg(new FuPacketOutV10().setXid(requestXid).writeTo());
                }
                log.info("state send "+strategy.actionValue+" PACKET_OUT packet to switch.");
                break;
            case DROP:
                return;
        }
    }

    public void sendBarrierRequesttoController() {
        switch (strategy.action) {
            case MODIFY:
                FuBarrierRequestV10 barrierRequest = new FuBarrierRequestV10();
                barrierRequest.xid = requestXid;
                barrierRequest.setField(strategy.field, strategy.actionValue);
                sendRawMsg(barrierRequest.writeTo());
                log.info("send a BarrierRequest to switch.");
                break;
            case DELAY:
                try {
                    Thread.sleep(Long.parseLong(strategy.actionValue));
                } catch (Exception e) {
                    log.info(e.toString());
                }
                sendRawMsg(new FuBarrierRequestV10().setXid(requestXid).writeTo());
                log.info("send a BarrierRequest to switch.");
                break;
            case DUPLICATE:
                for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                    sendRawMsg(new FuBarrierRequestV10().setXid(requestXid).writeTo());
                }
                log.info("state send "+strategy.actionValue+" BarrierRequest packet to switch.");
                break;
            case DROP:
                return;
        }
    }


    public void sendQueueGetConfigRequesttoController() {
        switch (strategy.action) {
            case MODIFY:
                FuQueueGetConfigRequestV10 queueGetConfigRequest = new FuQueueGetConfigRequestV10();
                queueGetConfigRequest.xid = requestXid;
                queueGetConfigRequest.setField(strategy.field, strategy.actionValue);
                sendRawMsg(queueGetConfigRequest.writeTo());
                log.info("send a QueueGetConfigRequest to switch.");
                break;
            case DELAY:
                try {
                    Thread.sleep(Long.parseLong(strategy.actionValue));
                } catch (Exception e) {
                    log.info(e.toString());
                }
                sendRawMsg(new FuQueueGetConfigRequestV10().setXid(requestXid).writeTo());
                log.info("send a QueueGetConfigRequest to switch.");
                break;
            case DUPLICATE:
                for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                    sendRawMsg(new FuQueueGetConfigRequestV10().setXid(requestXid).writeTo());
                }
                log.info("state send "+strategy.actionValue+" QueueGetConfigRequest packet to switch.");
                break;
            case DROP:
                return;
        }
    }

    public void sendGetConfigRequesttoController() {
        switch (strategy.action) {
            case MODIFY:
                FuGetConfigRequestV10 getConfigRequest = new FuGetConfigRequestV10();
                getConfigRequest.xid = requestXid;
                getConfigRequest.setField(strategy.field, strategy.actionValue);
                sendRawMsg(getConfigRequest.writeTo());
                log.info("send a GetConfigRequest to switch.");
                break;
            case DELAY:
                try {
                    Thread.sleep(Long.parseLong(strategy.actionValue));
                } catch (Exception e) {
                    log.info(e.toString());
                }
                sendRawMsg(new FuGetConfigRequestV10().setXid(requestXid).writeTo());
                log.info("send a GetConfigRequest to switch.");
                break;
            case DUPLICATE:
                for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                    sendRawMsg(new FuGetConfigRequestV10().setXid(requestXid).writeTo());
                }
                log.info("state send "+strategy.actionValue+" GetConfigRequest packet to switch.");
                break;
            case DROP:
                return;
        }
    }

    public void sendSETCONFIGtoController() {
        switch (strategy.action) {
            case MODIFY:
                FuSetConfigV10 setConfig = new FuSetConfigV10();
                setConfig.xid = requestXid;
                setConfig.setField(strategy.field, strategy.actionValue);
                sendRawMsg(setConfig.writeTo());
                log.info("send a SET_CONFIG to switch.");
                break;
            case DELAY:
                try {
                    Thread.sleep(Long.parseLong(strategy.actionValue));
                } catch (Exception e) {
                    log.info(e.toString());
                }
                try {
                    sendSetConfig(requestXid);
                    log.info("send a SET_CONFIG to switch.");
                } catch (OFParseError ofParseError) {
                    log.error(ofParseError.toString());
                }
                break;
            case DUPLICATE:
                try {
                    for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                        sendSetConfig(requestXid);
                    }
                    log.info("state send "+strategy.actionValue+" SET_CONFIG packet to switch.");
                } catch (OFParseError ofParseError) {
                    log.error(ofParseError.toString());
                }
                break;
            case DROP:
                return;
        }
    }

    public void sendPORTMODtoController() {
        switch (strategy.action) {
            case MODIFY:
                FuPortModV10 portMod = new FuPortModV10();
                portMod.xid = requestXid;
                portMod.setField(strategy.field, strategy.actionValue);
                sendRawMsg(portMod.writeTo());
                log.info("send a PORT_MOD to switch.");
                break;
            case DELAY:
                try {
                    Thread.sleep(Long.parseLong(strategy.actionValue));
                } catch (Exception e) {
                    log.info(e.toString());
                }
                sendRawMsg(new FuPortModV10().setXid(requestXid).writeTo());
                log.info("send a PORT_MOD to switch.");
                break;
            case DUPLICATE:
                for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                    sendRawMsg(new FuPortModV10().setXid(requestXid).writeTo());
                }
                log.info("state send "+strategy.actionValue+" PORT_MOD packet to switch.");
                break;
            case DROP:
                return;
        }
    }

    public void sendERRORtoController() {
        switch (strategy.action) {
            case MODIFY:
                FuErrorV10 error = new FuErrorV10();
                error.xid = requestXid;
                error.setField(strategy.field, strategy.actionValue);
                sendRawMsg(error.writeTo());
                log.info("send a ERROR to switch.");
                break;
            case DELAY:
                try {
                    Thread.sleep(Long.parseLong(strategy.actionValue));
                } catch (Exception e) {
                    log.info(e.toString());
                }
                sendRawMsg(new FuErrorV10().setXid(requestXid).writeTo());
                log.info("state send a ERROR packet to switch.");
                break;
            case DUPLICATE:
                for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                    sendRawMsg(new FuErrorV10().setXid(requestXid).writeTo());
                }
                log.info("state send "+strategy.actionValue+" ERROR packet to switch.");
                break;
            case DROP:
                return;
        }
    }

    public void sendStatReqtoController() {
        switch (strategy.childType) {
            case "PORT_REQUEST" :
                switch (strategy.action) {
                    case MODIFY:
                        FuPortStatsRequestV10 portStatsRequest = new FuPortStatsRequestV10();
                        portStatsRequest.xid = requestXid;
                        portStatsRequest.setField(strategy.field, strategy.actionValue);
                        sendRawMsg(portStatsRequest.writeTo());
                        log.info("send a ERROR to switch.");
                        break;
                    case DELAY:
                        try {
                            Thread.sleep(Long.parseLong(strategy.actionValue));
                        } catch (Exception e) {
                            log.info(e.toString());
                        }
                        sendRawMsg(new FuPortStatsRequestV10().setXid(requestXid).writeTo());
                        log.info("state send a ERROR packet to switch.");
                        break;
                    case DUPLICATE:
                        for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                            sendRawMsg(new FuPortStatsRequestV10().setXid(requestXid).writeTo());
                        }
                        log.info("state send "+strategy.actionValue+" ERROR packet to switch.");
                        break;
                    case DROP:
                        return;
                }
            case "FLOW_REQUEST":
                switch (strategy.action) {
                    case MODIFY:
                        FuFlowStatsRequestV10 flowStatsRequest = new FuFlowStatsRequestV10();
                        flowStatsRequest.xid = requestXid;
                        flowStatsRequest.setField(strategy.field, strategy.actionValue);
                        sendRawMsg(flowStatsRequest.writeTo());
                        log.info("send a ERROR to switch.");
                        break;
                    case DELAY:
                        try {
                            Thread.sleep(Long.parseLong(strategy.actionValue));
                        } catch (Exception e) {
                            log.info(e.toString());
                        }
                        sendRawMsg(new FuFlowStatsRequestV10().setXid(requestXid).writeTo());
                        log.info("state send a ERROR packet to switch.");
                        break;
                    case DUPLICATE:
                        for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                            sendRawMsg(new FuFlowStatsRequestV10().setXid(requestXid).writeTo());
                        }
                        log.info("state send "+strategy.actionValue+" ERROR packet to switch.");
                        break;
                    case DROP:
                        return;
                }
            default:
                log.error("no matched STATS_REQUEST to process.");
        }
    }

    public void sendFLOWMODtoController() {
        switch (strategy.action) {
            case MODIFY:
                FuFlowModV10 flowMod = new FuFlowModV10();
                flowMod.xid = requestXid;
                flowMod.setField(strategy.field, strategy.actionValue);
                sendRawMsg(flowMod.writeTo());
                log.info("send a FLOW_MOD to switch.");
                break;
            case DELAY:
                try {
                    Thread.sleep(Long.parseLong(strategy.actionValue));
                } catch (Exception e) {
                    log.info(e.toString());
                }
                sendRawMsg(new FuFlowModV10().setXid(requestXid).writeTo());
                log.info("send a FLOW_MOD to switch.");
                break;
            case DUPLICATE:
                for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                    sendRawMsg(new FuFlowModV10().setXid(requestXid).writeTo());
                }
                log.info("state send "+strategy.actionValue+" FLOW_MOD packet to switch.");
                break;
            case DROP:
                return;
        }
    }

    public void sendECHOREQUESTtoController() {
        switch (strategy.action) {
            case MODIFY:
                FuEchoRequestV10 echoRequest = new FuEchoRequestV10();
                echoRequest.xid = requestXid;
                echoRequest.setField(strategy.field, strategy.actionValue);
                sendRawMsg(echoRequest.writeTo());
                log.info("send a ECHO_REQUEST to switch.");
                break;
            case DELAY:
                try {
                    Thread.sleep(Long.parseLong(strategy.actionValue));
                } catch (Exception e) {
                    log.info(e.toString());
                }
                sendRawMsg(new FuEchoRequestV10().setXid(requestXid).writeTo());
                log.info("state send a ECHO_REQUEST packet to switch.");
                break;
            case DUPLICATE:
                for(int i = 0; i<Integer.parseInt(strategy.actionValue); i++) {
                    sendRawMsg(new FuEchoRequestV10().setXid(requestXid).writeTo());
                }
                log.info("state send "+strategy.actionValue+" ECHO_REQUEST packet to switch.");
                break;
            case DROP:
                return;
        }
    }

    public void sendMsg(OFMessage msg, int len) {
        ByteBuf buf;

        if (len == -1) {
            buf = PooledByteBufAllocator.DEFAULT.directBuffer(1024);
        } else {
            buf = PooledByteBufAllocator.DEFAULT.directBuffer(len);
        }

        msg.writeTo(buf);

        int length = buf.readableBytes();
        byte[] bytes = new byte[length];
        buf.getBytes(buf.readerIndex(), bytes);

        if(targetSock.isClosed()) {
            log.info("targetSock is closed, could not sendMsg.");
            return;
        }

        try {
            this.out.write(bytes, 0, length);
        } catch (IOException e) {
            e.printStackTrace();
        }

        buf.clear();
        buf.release();
    }

    public void sendRawMsg(byte[] msg) {
        if(targetSock.isClosed()) {
            log.info("targetSock is closed, could not sendMsg.");
            return;
        }
        try {
            this.out.write(msg, 0, msg.length);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void sendRawMsg(ByteBuf buf) {
        int length = buf.readableBytes();
        byte[] bytes = new byte[length];
        buf.getBytes(buf.readerIndex(), bytes);

        if(targetSock.isClosed()) {
            log.info("targetSock is closed, could not sendMsg.");
            return;
        }
        try {
            this.out.write(bytes, 0, length);
        } catch (IOException e) {
            e.printStackTrace();
        }

        buf.clear();
        buf.release();
    }

    public OFFlowAdd getBackupFlowAdd() {
        return backupFlowAdd;
    }

    public void sendExperimenter(long xid) {
        byte[] msg = DummyData.hexStringToByteArray(DummyData.VENDOR);
        byte[] xidbytes = Longs.toByteArray(xid);
        System.arraycopy(xidbytes, 4, msg, 4, 4);
        sendRawMsg(msg);
    }

    public void sendStatReq(long xid) throws OFParseError {
        byte[] msg = DummyData.hexStringToByteArray(DummyData.STATS_REQ);
        byte[] xidbytes = Longs.toByteArray(xid);
        System.arraycopy(xidbytes, 4, msg, 4, 4);
        sendRawMsg(msg);
    }

    public void sendSetConfig(long xid) throws OFParseError {
        OFSetConfig.Builder builder = factory.buildSetConfig();
        builder.setMissSendLen(0xffff);
        builder.setXid(xid);
        sendMsg(builder.build(), -1);
    }

    public void sendGetConfigReq(long xid) throws OFParseError {
        OFGetConfigRequest.Builder builder = factory.buildGetConfigRequest();
        builder.setXid(xid);

        sendMsg(builder.build(), -1);
    }

    public void sendFeatureReq(long xid) throws OFParseError {
        OFFeaturesRequest.Builder builder = factory.buildFeaturesRequest();
        builder.setXid(xid);
        sendMsg(builder.build(), -1);
    }

    public void sendEchoReply(long xid) {
        OFEchoReply.Builder builder = factory.buildEchoReply();
        builder.setXid(xid);

        sendMsg(builder.build(), -1);
    }

    public void sendHello(long xid) {
        OFHello.Builder fab = factory.buildHello();
        fab.setXid(xid);

        if (version != OFVersion.OF_10) {
            OFHelloElem.Builder heb = factory.buildHelloElemVersionbitmap();
            ((OFHelloElemVersionbitmap.Builder) heb).setBitmaps(Collections.singletonList(U32.of(0x12)));
            List<OFHelloElem> list = new ArrayList<OFHelloElem>();
            list.add(heb.build());
            fab.setElements(list);
        }

        OFHello hello = fab.build();
        sendMsg(hello, -1);
    }

    public void addSynMsg() {
        needSynMsg.add(OFType.PACKET_OUT);
        needSynMsg.add(OFType.FLOW_MOD);
        needSynMsg.add(OFType.SET_CONFIG);
        needSynMsg.add(OFType.BARRIER_REQUEST);
        needSynMsg.add(OFType.GET_CONFIG_REQUEST);
        needSynMsg.add(OFType.QUEUE_GET_CONFIG_REQUEST);
        needSynMsg.add(OFType.ECHO_REQUEST);
        needSynMsg.add(OFType.ERROR);
        needSynMsg.add(OFType.EXPERIMENTER);
        needSynMsg.add(OFType.STATS_REQUEST);
    }
}
